﻿using System.Reflection;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

namespace GameToolkit.Editor
{
    [CanEditMultipleObjects]
    [CustomEditor(typeof(VolumetricBounds), true)]
    public class VolumeBoundsEditor : UnityEditor.Editor
    {
        MethodInfo validateCall;
        GUIStyle titleStyle;

        public bool isEditingBounds
        {
            get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); }
        }

        protected Bounds GetBounds()
        {
            var volumn = (VolumetricBounds)target;
            return volumn.bounds;//  new Bounds(navModifier.transform.position, navModifier.size);
        }

        protected virtual void OnEnable()
        {
            if (titleStyle == null)
            {
                titleStyle = new GUIStyle();
            }
            validateCall = target.GetType().GetMethod("OnValidate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance);
        }

        public override void OnInspectorGUI()
        {
            var multiEdit = serializedObject.isEditingMultipleObjects;
            var vol = target as VolumetricBounds;
            var rect = EditorGUILayout.BeginHorizontal();
            var icon = EditorGUIUtility.IconContent("PreMatCube");
            GUI.DrawTexture(new Rect(rect.x, rect.y + 2, 16, 16), icon.image, ScaleMode.ScaleToFit, true, 0, multiEdit ? Color.gray : vol.color, 0, 0);
            EditorGUI.BeginDisabledGroup(multiEdit);
            EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume",
                EditorGUIUtility.IconContent("EditCollider"), GetBounds, this);
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndHorizontal();

            base.OnInspectorGUI();
        }

        protected virtual void OnDisable() { }

        public static void HandleDottedCube(Vector3 center, Vector3 size, float screenSpaceSize)
        {
            var min = center - size * 0.5f;
            var max = min + size;
            Handles.DrawDottedLine(min, min + Vector3.right * size.x, screenSpaceSize);
            Handles.DrawDottedLine(min, min + Vector3.forward * size.z, screenSpaceSize);
            Handles.DrawDottedLine(min, min + Vector3.up * size.y, screenSpaceSize);
            Handles.DrawDottedLine(max, max + Vector3.left * size.x, screenSpaceSize);
            Handles.DrawDottedLine(max, max + Vector3.back * size.z, screenSpaceSize);
            Handles.DrawDottedLine(max, max + Vector3.down * size.y, screenSpaceSize);

            Handles.DrawDottedLine(min + Vector3.up * size.y, max + Vector3.left * size.x, screenSpaceSize);
            Handles.DrawDottedLine(min + Vector3.forward * size.z, max + Vector3.left * size.x, screenSpaceSize);
            Handles.DrawDottedLine(min + Vector3.forward * size.z, max + Vector3.down * size.y, screenSpaceSize);
            Handles.DrawDottedLine(min + Vector3.up * size.y, max + Vector3.back * size.z, screenSpaceSize);
            Handles.DrawDottedLine(min + Vector3.right * size.x, max + Vector3.back * size.z, screenSpaceSize);
            Handles.DrawDottedLine(min + Vector3.right * size.x, max + Vector3.down * size.y, screenSpaceSize);
        }

        protected void HandleBoundsGUI(VolumetricBounds vol)
        {
            bool dirty = false;
            var rot = vol.transform.rotation;
            Handles.matrix = vol.IsUnityScale ? Matrix4x4.TRS(vol.transform.position, rot, Vector3.one) : vol.transform.localToWorldMatrix;
            rot.w = -rot.w;
            Matrix4x4 toLocal = vol.IsUnityScale ? Matrix4x4.TRS(-vol.transform.position, rot, Vector3.one) : vol.transform.worldToLocalMatrix;
            var col1 = vol.color;
            var col2 = col1 * 0.5f;
            var cam = toLocal.MultiplyPoint(SceneView.currentDrawingSceneView.camera.transform.position);
            var up = vol.m_BoundsCenter + Vector3.up * vol.m_BoundsSize.y * 0.5f;
            var down = vol.m_BoundsCenter + Vector3.down * vol.m_BoundsSize.y * 0.5f;
            var left = vol.m_BoundsCenter + Vector3.left * vol.m_BoundsSize.x * 0.5f;
            var right = vol.m_BoundsCenter + Vector3.right * vol.m_BoundsSize.x * 0.5f;
            var forward = vol.m_BoundsCenter + Vector3.forward * vol.m_BoundsSize.z * 0.5f;
            var back = vol.m_BoundsCenter + Vector3.back * vol.m_BoundsSize.z * 0.5f;

            Handles.color = Vector3.Dot(cam - up, Vector3.up) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            var point = Handles.FreeMoveHandle(up, HandleUtility.GetHandleSize(up) * 0.04f, Vector3.up * 0.01f, Handles.DotHandleCap);
#else
            var point = Handles.FreeMoveHandle(up, Quaternion.identity, HandleUtility.GetHandleSize(up) * 0.04f, Vector3.up * 0.01f, Handles.DotHandleCap);
#endif
            point.y = Mathf.Max(down.y, point.y);
            dirty |= Mathf.Abs(point.y - up.y) >= 0.001f;
            up = point;

            Handles.color = Vector3.Dot(cam - down, Vector3.down) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            point = Handles.FreeMoveHandle(down, HandleUtility.GetHandleSize(down) * 0.04f, Vector3.down * 0.01f, Handles.DotHandleCap);
#else
            point = Handles.FreeMoveHandle(down, Quaternion.identity, HandleUtility.GetHandleSize(down) * 0.04f, Vector3.down * 0.01f, Handles.DotHandleCap);
#endif
            point.y = Mathf.Min(up.y, point.y);
            dirty |= Mathf.Abs(point.y - down.y) >= 0.001f;
            down = point;

            Handles.color = Vector3.Dot(cam - left, Vector3.left) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            point = Handles.FreeMoveHandle(left, HandleUtility.GetHandleSize(left) * 0.04f, Vector3.left * 0.01f, Handles.DotHandleCap);
#else
            point = Handles.FreeMoveHandle(left, Quaternion.identity, HandleUtility.GetHandleSize(left) * 0.04f, Vector3.left * 0.01f, Handles.DotHandleCap);
#endif
            point.x = Mathf.Min(right.x, point.x);
            dirty |= Mathf.Abs(point.x - left.x) >= 0.001f;
            left = point;

            Handles.color = Vector3.Dot(cam - right, Vector3.right) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            point = Handles.FreeMoveHandle(right, HandleUtility.GetHandleSize(right) * 0.04f, Vector3.right * 0.01f, Handles.DotHandleCap);
#else
            point = Handles.FreeMoveHandle(right, Quaternion.identity, HandleUtility.GetHandleSize(right) * 0.04f, Vector3.right * 0.01f, Handles.DotHandleCap);
#endif
            point.x = Mathf.Max(left.x, point.x);
            dirty |= Mathf.Abs(point.x - right.x) >= 0.001f;
            right = point;

            Handles.color = Vector3.Dot(cam - back, Vector3.back) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            point = Handles.FreeMoveHandle(back, HandleUtility.GetHandleSize(back) * 0.04f, Vector3.back * 0.01f, Handles.DotHandleCap);
#else
            point = Handles.FreeMoveHandle(back, Quaternion.identity, HandleUtility.GetHandleSize(back) * 0.04f, Vector3.back * 0.01f, Handles.DotHandleCap);
#endif
            point.z = Mathf.Min(forward.z, point.z);
            dirty |= Mathf.Abs(point.z - back.z) >= 0.001f;
            back = point;

            Handles.color = Vector3.Dot(cam - forward, Vector3.forward) > 0 ? col1 : col2;
#if UNITY_2022_1_OR_NEWER
            point = Handles.FreeMoveHandle(forward, HandleUtility.GetHandleSize(forward) * 0.04f, Vector3.forward * 0.01f, Handles.DotHandleCap);
#else
            point = Handles.FreeMoveHandle(forward, Quaternion.identity, HandleUtility.GetHandleSize(forward) * 0.04f, Vector3.forward * 0.01f, Handles.DotHandleCap);
#endif
            point.z = Mathf.Max(back.z, point.z);
            dirty |= Mathf.Abs(point.z - forward.z) >= 0.001f;
            forward = point;

            if (dirty)
            {
                Undo.RecordObject(vol, "Modify Volume");
                vol.m_BoundsCenter = new Vector3((left.x + right.x) * 0.5f, (down.y + up.y) * 0.5f, (forward.z + back.z) * 0.5f);
                vol.m_BoundsSize = new Vector3(right.x - left.x, up.y - down.y, forward.z - back.z);
                EditorUtility.SetDirty(vol);
                if (validateCall != null)
                    validateCall.Invoke(vol, null);

            }
        }

        protected virtual void OnSceneGUI()
        {
            var vol = (VolumetricBounds)target;
            vol.IsEditMode = isEditingBounds;
            if (vol.IsEditMode)
                HandleBoundsGUI(vol);
        }

    }
}